package br.ufal.tci.nexos.arcolive.beans;

import java.awt.Component;
import java.io.IOException;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Vector;

import javax.media.CannotRealizeException;
import javax.media.Format;
import javax.media.Manager;
import javax.media.NoProcessorException;
import javax.media.Processor;
import javax.media.ProcessorModel;
import javax.media.control.BufferControl;
import javax.media.format.AudioFormat;
import javax.media.format.UnsupportedFormatException;
import javax.media.format.VideoFormat;
import javax.media.protocol.ContentDescriptor;
import javax.media.protocol.DataSource;
import javax.media.protocol.PushBufferDataSource;
import javax.media.protocol.PushBufferStream;
import javax.media.protocol.SourceCloneable;
import javax.media.rtp.InvalidSessionAddressException;
import javax.media.rtp.RTPManager;
import javax.media.rtp.ReceiveStream;
import javax.media.rtp.ReceiveStreamListener;
import javax.media.rtp.ReceptionStats;
import javax.media.rtp.SendStream;
import javax.media.rtp.SessionAddress;
import javax.media.rtp.event.ApplicationEvent;
import javax.media.rtp.event.NewReceiveStreamEvent;
import javax.media.rtp.event.ReceiveStreamEvent;

/**
 * CLASSNAME.java
 * 
 * CLASS DESCRIPTION
 * 
 * @see CLASSNAME
 * 
 * @author <a href="mailto:felipe@labpesquisas.tci.ufal.br">Felipe Barros Pontes</a>.
 * @author <a href="mailto:leandro@labpesquisas.tci.ufal.br">Leandro Melo de Sales</a>.
 * @since 0.1
 * @version 0.1
 * 
 * <p>
 * <b>Revisions:</b>
 * 
 * <p>
 * <b>yyyymmdd USERNAME:</b>
 * <ul>
 * <li> VERSION
 * </ul>
 */

/**
 * @author <a href="mailto:felipe@labpesquisas.tci.ufal.br">Felipe Barros Pontes</a>.
 * @author <a href="mailto:leandro@labpesquisas.tci.ufal.br">Leandro Melo de
 *         Sales</a>.
 */
public class NetworkPlayer extends ArCoLIVEPlayer implements
		Serializable, ReceiveStreamListener {

	private RTPManager rtpTransmitManagers[];

	private RTPManager rtpReceiveManager;

	private ReceptionStats receptionStats;

	private DataSource receptionDataSource;

	private DataSource transmissionDataSource;

	private Processor processor;

	private int tracksCount;

	private Vector arcoliveNetworkPlayerListeners;

	private int transmissionPort = 40000;

	private int receptionPort = 40020;
	
	private int bufferLength;

	private String receptorHost = "192.168.7.55";

	private String senderHost = "192.168.7.55";

	private String videoFormatType = null;

	private String audioFormatType = null;

	private Component receptionBufferControlComponent;

	private Component[] transmissionBufferControlComponents;

	/**
	 * 
	 */
	public NetworkPlayer() {
		super();
		this.arcoliveNetworkPlayerListeners = new Vector();
	}

	/**
	 * @return
	 */
	public String getAudioFormatType() {
		return audioFormatType;
	}

	/**
	 * @param audioFormatType
	 */
	public void setAudioFormatType(String audioFormatType) {
		this.audioFormatType = audioFormatType;
	}

	/**
	 * @return
	 */
	public int getBufferLength() {
		return bufferLength;
	}

	/**
	 * @param bufferLength
	 */
	public void setBufferLength(int bufferLength) {
		this.bufferLength = bufferLength;
	}

	/**
	 * @return
	 */
	public int getReceptionPort() {
		return receptionPort;
	}

	/**
	 * @param receptionPort
	 */
	public void setReceptionPort(int receptionPort) {
		this.receptionPort = receptionPort;
	}

	/**
	 * @return
	 */
	public String getReceptorHost() {
		return receptorHost;
	}

	/**
	 * @param receptorHosts
	 */
	public void setReceptorHost(String receptorHost) {
		this.receptorHost = receptorHost;
	}

	/**
	 * @return
	 */
	public String getSenderHost() {
		return senderHost;
	}

	/**
	 * @param senderHosts
	 */
	public void setSenderHost(String senderHost) {
		this.senderHost = senderHost;
	}

	/**
	 * @return
	 */
	public int getTransmissionPort() {
		return transmissionPort;
	}

	/**
	 * @param transmissionPort
	 */
	public void setTransmissionPort(int transmissionPort) {
		this.transmissionPort = transmissionPort;
	}

	/**
	 * @return
	 */
	public String getVideoFormatType() {
		return videoFormatType;
	}

	/**
	 * @param videoFormatType
	 */
	public void setVideoFormatType(String videoFormatType) {
		this.videoFormatType = videoFormatType;
	}

	/**
	 * @return
	 */
	private Format[] getFormats() {
		VideoFormat videoFormat = null;
		AudioFormat audioFormat = null;
		Format[] formats = null;
		if (this.videoFormatType != null && this.audioFormatType != null) {
			videoFormat = new VideoFormat(this.videoFormatType);
			audioFormat = new AudioFormat(this.audioFormatType);
			formats = new Format[] { videoFormat, audioFormat };
		} else if (this.videoFormatType != null && this.audioFormatType == null) {
			videoFormat = new VideoFormat(this.videoFormatType);
			formats = new Format[] { videoFormat };
		} else if (this.videoFormatType == null && this.audioFormatType != null) {
			audioFormat = new AudioFormat(this.audioFormatType);
			formats = new Format[] { audioFormat };
		}
		return formats;
	}

	/**
	 * @throws CannotTransmitStreamException
	 */
	public void transmitStream() throws CannotTransmitStreamException {
		String message = "Cannot transmit stream: ";
		int port;
		SessionAddress localAddress, targetAddress;
		SendStream sendStream;
		ContentDescriptor rtpContainer = new ContentDescriptor(
				ContentDescriptor.RAW_RTP);
		Format formats[] = this.getFormats();
            	ProcessorModel rtpProcessorModel = new ProcessorModel(super
				.getMediaLocator(), formats, rtpContainer);
		try {
			processor = Manager.createRealizedProcessor(rtpProcessorModel);
			this.transmissionDataSource = processor.getDataOutput();
			PushBufferDataSource pbds = (PushBufferDataSource) this.transmissionDataSource;
			PushBufferStream pbss[] = pbds.getStreams();
			this.tracksCount = pbss.length;
			this.transmissionBufferControlComponents = new Component[this.tracksCount];
			this.rtpTransmitManagers = new RTPManager[this.tracksCount];
			for (int i = 0; i < this.tracksCount; i++) {
				port = transmissionPort + 2 * i;
				this.rtpTransmitManagers[i] = RTPManager.newInstance();
				localAddress = new SessionAddress(InetAddress.getLocalHost(),
						port);
				this.rtpTransmitManagers[i].initialize(localAddress);
				targetAddress = new SessionAddress(InetAddress
						.getByName(receptorHost), port);
				this.rtpTransmitManagers[i].addTarget(targetAddress);
				sendStream = this.rtpTransmitManagers[i].createSendStream(
						this.transmissionDataSource, i);
				sendStream.start();
				BufferControl bufferControl = (BufferControl) this.rtpTransmitManagers[i]
						.getControl("javax.media.control.BufferControl");
				this.transmissionBufferControlComponents[i] = bufferControl
						.getControlComponent();
				if (bufferControl != null) {
					bufferControl.setBufferLength(this.getBufferLength());
				}
			}
			this.processor.start();
		} catch (NoProcessorException e) {
			throw new CannotTransmitStreamException(message + e.getMessage());
		} catch (CannotRealizeException e) {
			throw new CannotTransmitStreamException(message + e.getMessage());
		} catch (IOException e) {
			throw new CannotTransmitStreamException(message + e.getMessage());
		} catch (InvalidSessionAddressException e) {
			throw new CannotTransmitStreamException(message + e.getMessage());
		} catch (UnsupportedFormatException e) {
			throw new CannotTransmitStreamException(message + e.getMessage());
		}
	}

	/**
	 * @throws CannotHandleStreamException
	 */
	public void handleStream() throws CannotHandleStreamException {
		String message = "Cannot handle stream: ";
		try {
			this.rtpReceiveManager = (RTPManager) RTPManager.newInstance();
			this.rtpReceiveManager.addReceiveStreamListener(this);
			rtpReceiveManager
					.initialize(new RTPConnectorAdapter(InetAddress
							.getByName(this.senderHost), this.receptionPort));
			BufferControl bufferControl = (BufferControl) rtpReceiveManager
					.getControl("javax.media.control.BufferControl");
			this.receptionBufferControlComponent = bufferControl
					.getControlComponent();
			if (bufferControl != null) {
				bufferControl.setBufferLength(this.getBufferLength());
			}
		} catch (UnknownHostException e) {
			throw new CannotHandleStreamException(message + e.getMessage());
		} catch (IOException e) {
			throw new CannotHandleStreamException(message + e.getMessage());
		}
	}

	/**
	 * @return
	 */
	public Component getReceptionBufferControlComponent() {
		return this.receptionBufferControlComponent;
	}

	/**
	 * @return
	 */
	public Component[] getTransmissionBufferControlComponent() {
		return this.transmissionBufferControlComponents;
	}

	/**
	 * @return
	 */
	public DataSource getReceiveDataSource() {
		return this.receptionDataSource;
	}

	/**
	 * @return
	 */
	public ReceptionStats getReceptionStats() {
		return this.receptionStats;
	}

	/**
	 * @throws CannotStopTransmissionException
	 */
	public void stopTrasmission() throws CannotStopTransmissionException {
		String message = "Cannot stop transmission: ";
		try {
			int rtpTransmitManagersCount = this.rtpTransmitManagers.length;
			Vector sendStreams = this.rtpReceiveManager.getSendStreams();
			for (int i = 0; i < rtpTransmitManagersCount; i++) {
				this.rtpTransmitManagers[i]
						.removeTargets("Transmission stoped!");
				int sendStreamsCount = sendStreams.size();
				for (int j = 0; j < sendStreamsCount; j++) {
					((SendStream) sendStreams.get(j)).stop();
				}
			}
		} catch (IOException e) {
			throw new CannotStopTransmissionException(message + e.getMessage());
		}
	}

	/**
	 * 
	 */
	public void stopReception() {
		this.rtpReceiveManager.removeTargets("Reception stoped!");
	}

	/**
	 * 
	 */
	public void stopPlayer() {
		super.stop();
		super.eject();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.media.rtp.ReceiveStreamListener#update(javax.media.rtp.event.ReceiveStreamEvent)
	 */
	public void update(ReceiveStreamEvent receiveStreamEvent) {
		ReceiveStream stream;
		if (receiveStreamEvent instanceof NewReceiveStreamEvent) {
			stream = ((NewReceiveStreamEvent) receiveStreamEvent)
					.getReceiveStream();
			this.receptionDataSource = stream.getDataSource();
			if (this.receptionDataSource != null) {
				this.notifyDataSourceReadyEvent();
			}
		}
		if (receiveStreamEvent instanceof ApplicationEvent) {
			this.receptionStats = receiveStreamEvent.getReceiveStream()
					.getSourceReceptionStats();
			if (this.receptionStats != null) {
				this.notifyReceptionStatsReadyEvent();
			}
		}
	}

	/**
	 * @param arcoliveNetworkPlayerListener
	 */
	public synchronized void addArcoliveNetworkPlayerListener(
			NetworkPlayerListener arcoliveNetworkPlayerListener) {
		this.arcoliveNetworkPlayerListeners.add(arcoliveNetworkPlayerListener);
	}

	/**
	 * @param arcoliveNetworkPlayerListener
	 */
	public synchronized void removeArcoliveNetworkPlayerListener(
			NetworkPlayerListener arcoliveNetworkPlayerListener) {
		this.arcoliveNetworkPlayerListeners
				.remove(arcoliveNetworkPlayerListener);
	}

	/**
	 * 
	 */
	public synchronized void notifyDataSourceReadyEvent() {
		Vector currentListeners = null;
		currentListeners = (Vector) this.arcoliveNetworkPlayerListeners.clone();
		NetworkPlayerEvent arcoliveNetworkPlayerEvent = new NetworkPlayerEvent(
				this);
		int size = this.arcoliveNetworkPlayerListeners.size();
		for (int i = 0; i < size; i++) {
			((NetworkPlayerListener) currentListeners.elementAt(i))
					.dataSourceReadyEvent(arcoliveNetworkPlayerEvent);
		}
	}

	/**
	 * 
	 */
	public synchronized void notifyReceptionStatsReadyEvent() {
		Vector currentListeners = null;
		currentListeners = (Vector) this.arcoliveNetworkPlayerListeners.clone();
		NetworkPlayerEvent arcoliveNetworkPlayerEvent = new NetworkPlayerEvent(
				this);
		int size = this.arcoliveNetworkPlayerListeners.size();
		for (int i = 0; i < size; i++) {
			((NetworkPlayerListener) currentListeners.elementAt(i))
					.receptionStatsReadyEvent(arcoliveNetworkPlayerEvent);
		}
	}

}